package com.atguigu.gmall.realtime.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import netscape.javascript.JSObject;
import org.apache.flink.api.java.tuple.Tuple2;
import redis.clients.jedis.Jedis;

import java.util.List;

/**
 * @Description: 查询维度表中的维度数据工具类
 * @Author: tiancy
 * @Create: 2021/12/1
 */

public class DimUtil {

    /**
     * 指定表和查询的 (列名和列值) 直接从phoenix中查询,要么返回一条,要么查询不到.
     *
     * @param : tableName
     * @param : colNameAndValue
     * @return : JSONObject
     */
    public static JSONObject getDimInfoNoCache(String tableName, Tuple2<String, String>... colNameAndValues) {
        // select * from DIM_BASE_TRADEMARK where ID = '13' and TM_NAME = '巴黎世家';
        StringBuilder queryConditionsSQL = new StringBuilder("select * from " + tableName + " where ");

        for (int i = 0; i < colNameAndValues.length; i++) {
            Tuple2<String, String> colNameAndValue = colNameAndValues[i];
            String colName = colNameAndValue.f0;
            String colValue = colNameAndValue.f1;
            queryConditionsSQL.append(colName + "='" + colValue + "'");
            if (i < colNameAndValues.length - 1) {
                queryConditionsSQL.append(" and ");
            }
        }
        System.out.println("查询维度SQL:" + queryConditionsSQL);
        JSONObject dimInfoJsonObj = null;

        List<JSONObject> dimList = PhoenixUtil.queryList(queryConditionsSQL.toString(), JSONObject.class);
        if (dimList != null && dimList.size() > 0) {
            //因为关联维度，肯定都是根据key关联得到一条记录
            dimInfoJsonObj = dimList.get(0);
        } else {
            System.out.println("维度数据未找到:" + queryConditionsSQL);
            return null;
        }
        return dimInfoJsonObj;
    }

    /**
     * 关联维度数据的工具类,加入旁路缓存优化. 先去`redis`查询是否存在缓存,如果有,直接取.如果没有再去HBase中查询,查询结果返回 并存到 `redis`.
     *
     * @param tableName
     * @param colNameAndValues
     * @return JSONObject对象
     * <p>
     * redis缓存的设置( TTL过期时间为1天 )
     * 明确使用什么数据类型进行缓存, key是什么,value是什么.这里通过dim工具类去HBase中查询数据的结果,是一个`JsonObject`,直接转化为字符串,再存即可.
     * 因此 key = dim:表名:查询字段. value = 当前字段查询的结果 JSONObject.toString.
     * 举例 : 通过 phoenix查询HBase中, select * from DIM_BASE_TRADEMARK where ID = '13'
     * 则 redis中应该缓存的 key = dim:DIM_BASE_TRADEMARK:13 . value ="{"ID":"13","TM_NAME":"巴黎世家"}"
     * 如果查询是根据多个条件查询的,比如 : getDimInfo("DIM_BASE_TRADEMARK", Tuple2.of("ID", "13"), Tuple2.of("TM_NAME", "巴黎世家"))这种,则最终redis缓存的格式为:
     * key = "dim:DIM_BASE_TRADEMARK:13_巴黎世家", value = "{"ID":"13","TM_NAME":"巴黎世家"}"
     */
    public static JSONObject getDimInfo(String tableName, Tuple2<String, String>... colNameAndValues) {
        /** 先定义一个查询Redis的工具类,通过key尝试命中缓存,如果缓存没有命中,则再查询HBase.查询的结果再写入到redis中 */
        StringBuilder cacheKey = new StringBuilder("dim:" + tableName.toLowerCase() + ":"); // dim:表名:主键值1_主键值2
        StringBuilder queryConditionsSQL = new StringBuilder("select * from " + tableName + " where ");

        for (int i = 0; i < colNameAndValues.length; i++) {
            Tuple2<String, String> colNameAndValue = colNameAndValues[i];
            String colName = colNameAndValue.f0;
            String colValue = colNameAndValue.f1;
            cacheKey.append(colValue);
            queryConditionsSQL.append(colName + "='" + colValue + "'");
            if (i < colNameAndValues.length - 1) {
                queryConditionsSQL.append(" and ");
                cacheKey.append("_");
            }
        }

        // System.out.println(queryConditionsSQL);
        // System.out.println(cacheKey);   dim:DIM_BASE_TRADEMARK:13_巴黎世家
        /*
            编写 com.atguigu.gmall.realtime.utils.RedisUtil工具类,查询redis
            如果查询结果存在,则直接返回.如果查询结果不存在,再查询Hbase.
         */

        //声明操作Redis的客户端
        Jedis jedis = null;
        //声明一个字符串  用于接收从Redis中查询出来的维度数据
        String queryCacheRedisResult = null;
        //声明一个json对象，用于封装最后返回的维度信息
        JSONObject dimJsonObj = null;


        try {
            jedis = RedisUtil.getJedis();
            System.out.println("从Redis中查询数据的key是:" + cacheKey);
            queryCacheRedisResult = jedis.get(cacheKey.toString());
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("从Redis中查询维度数据发生了异常");
        }

        //判断是否从Redis中查询查询出了维度数据
        if (queryCacheRedisResult != null && queryCacheRedisResult.length() > 0) {
            //缓存命中 直接将缓存的内容(字符串)转换为json对象 ==> 直接通过 fastJson的 JSON.parseObject(str)
            dimJsonObj = JSON.parseObject(queryCacheRedisResult);
            /** 千万切记在使用redis这种,通过连接池的方式处理问题的时候,不能直接在if里面写 return 这种字眼,如果写了,缓存命中,下面的代码不执行.资源不能归还,导致资源耗尽.*/
            // return dimJsonObj;
        } else {
            //没有从Redis中查到数据，发送请求到phoenix表中查询
            System.out.println("从phoenix中查询维度的SQL语句为:" + queryConditionsSQL);

            //底层还是调用的phoenixUtil中的查询方法，从phoenix表中查询数据
            List<JSONObject> jsonObjList = PhoenixUtil.queryList(queryConditionsSQL.toString(), JSONObject.class);
            if (jsonObjList != null && jsonObjList.size() > 0) {
                //注意：因为我们是根据维度的主键去查询，所以如果存在维度数据的话，那么也只会有一条
                dimJsonObj = jsonObjList.get(0);

                //将从Phoenix中查询出来的数据放到Redis中缓存起来
                if (jedis != null) {
                    // 往redis中写数据, key,设置超时时间,value
                    jedis.setex(cacheKey.toString(), 3600 * 24, dimJsonObj.toJSONString());
                }
            } else {
                System.out.println("维度数据没找到:" + queryConditionsSQL);
            }
        }

        //释放资源
        if (jedis != null) {
            System.out.println("---关闭Jedis---");
            jedis.close();
        }
        return dimJsonObj;
    }

    /**
     * 当维度数据发生变化时,需要根据当前表名和主键id删除redis中缓存的数据.
     * 删除缓存的时机 : 当维度数据发生变化后,也就是业务数据经过分流,维度数据写入到`dwd_表名 这个主题`时,需要删除 `tim:表名:主键值`这个缓存数据.
     *
     * @param tableName
     * @param id
     */
    public static void deleteCached(String tableName, String id) {
        try {
            String redisKey = "dim:" + tableName.toLowerCase() + ":" + id;
            Jedis jedis = RedisUtil.getJedis();
            jedis.del(redisKey);
            jedis.close();
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("从Redis中删除缓存数据发生了异常");
        }
    }

    /**
     * 实现`getDimInfo`带缓存的查询dim维度层数据.给定查询的表名,给定查询表的主键,返回查询的dim维度表中的数据.
     *
     * @param tableName
     * @param id
     * @return
     */
    public static JSONObject getDimInfo(String tableName, String id) {
        return getDimInfo(tableName, Tuple2.of("ID", id));
    }

    public static void main(String[] args) {
        JSONObject dimInfoNoCache = getDimInfo("DIM_BASE_TRADEMARK", Tuple2.of("ID", "14"));
        System.out.println(dimInfoNoCache);
    }
}
